import { Injectable } from '@nestjs/common/decorators';
import { Logger } from '@nestjs/common/services';
import { InjectRepository } from '@nestjs/typeorm';
import { SchedulerRegistry } from '@nestjs/schedule';
import { AxiosError } from 'axios';
import {
  catchError,
  map,
  retry,
  switchMap,
  throwError,
  lastValueFrom,
} from 'rxjs';
import { User } from 'src/entities/user.entity';
import { Repository } from 'typeorm';
import { EmployeeService } from '../hrm/services/employee.service';
import { MDMAuthService } from '../mdmAuth/mdmAuth.service';
import { UserFromMDMDto } from '../user/user-from-mdm.dto';
import { IUser, UserStatusCodes } from '../user/user.interfaces';

@Injectable()
export class CronService {
  private logger = new Logger(CronService.name);
  constructor(
    @InjectRepository(User)
    private readonly userRepository: Repository<User>,
    private readonly employeeService: EmployeeService,
    private readonly mdmAuthService: MDMAuthService,
    private readonly schedulerRegistry: SchedulerRegistry,
  ) {}

  public async usersSync() {
    this.logger.log(`Start getting employee from CRON`);
    const usersSync = this.mdmAuthService.auth().pipe(
      map(({ data: { access_token } }) => access_token),
      switchMap((access_token) => this.employeeService.employees(access_token)),
      catchError((error: AxiosError) => throwError(() => error)),
      retry(1),
    );

    try {
      this.logger.log(`Start calling [usersSync] method`);
      const { data } = await lastValueFrom(usersSync);
      this.logger.log(`Users length from employees method: ${data.length}`);

      if (data && data.length) {
        // Устанавляваем hideEmployee для сотрудников с type = juridical с двумя учетками (virtual и juridical)
        this.logger.log(`Check double users: ${data.length}`);
        const prepareEmployee: UserFromMDMDto[] =
          this.employeeService.setHideEmployeeForDoubleEntry(data);
        this.logger.log(`Double users checked!`);

        this.logger.log(`Prepare users from MDM to save in DB`);
        const users: IUser[] = prepareEmployee.map((user) => {
          const mapUser: IUser = {
            id: user.id,
            lastNameRu: user.lastName,
            lastNameEn: user.lastNameEn,
            firstNameRu: user.firstName,
            firstNameEn: user.firstNameEn,
            positionId: user.positionId,
            positionNameRu: user.positionName,
            positionNameEn: null,
            unitId: user.unitId,
            unitNameRu: user.unitName,
            unitNameEn: null,
            head1NameRu: user.head1Name,
            head1NameEn: null,
            head1Email: user.head1Email,
            head1Id: user.head1Id,
            email: user.email,
            phone:
              Array.isArray(user.phones) && user.phones.length > 0
                ? user.phones.find(
                    (phone) => phone.type === UserStatusCodes.EMPLOYEE,
                  )
                  ? user.phones.find(
                      (phone) => phone.type === UserStatusCodes.EMPLOYEE,
                    ).phone
                  : null
                : null,
            workplace: null,
            birthDate: user.birthDate,
            orgUuid: user.orgUuid,
            orgNameRu: user.topUnit?.name ?? null,
            orgNameEn: null,
            hideEmployee: user.hideEmployee,
            cityRu: user.city,
            cityEn: null,
          };

          return mapUser;
        });
        this.logger.log(`Users mapped successfully!`);

        this.logger.log(`Get users from our db...`);
        // Получаем имеющихся в бд сотрудников
        const currentUsers = await this.userRepository.find();

        if (currentUsers) {
          this.logger.log(`Users got successfully!`);
          this.logger.log(`Calculate difference id between db and MDM...`);
          const hideUsers: IUser[] = this.employeeService.differenceEmployeeId(
            users,
            currentUsers,
          );
          this.logger.log(
            `Set hideEmployee true to employees who are in the database but did not come from the MDM `,
          );
          const usersData = this.employeeService.setHideEmployee(hideUsers);

          this.logger.log(`Saving hide users...`);
          const savedHideUsers = await this.userRepository.save(usersData, {
            chunk: 50,
          });
          this.logger.log(`${savedHideUsers.length} hide users saved!`);

          this.logger.log(`Saving users...`);
          const savedUsers = await this.userRepository.save(users, {
            chunk: 50,
          });

          this.logger.log(`${savedUsers.length} users saved!`);
        } else {
          this.logger.log(`Our db is empty!`);
          this.logger.log(`Saving users...`);
          const savedUsers = await this.userRepository.save(users, {
            chunk: 50,
          });

          this.logger.log(`${savedUsers.length} users saved!`);
        }
      }
      return { success: true };
    } catch (error) {
      this.logger.error(
        'Users sync error: ',
        error?.message,
        error?.response?.status,
        error?.response?.config?.url,
      );
      return { success: false };
    }
  }

  /**
   * On Module Init
   */
  public async startCronSync() {
    await this.usersSync();

    if (!JSON.parse(process.env.IS_DEV_MODE)) {
      this.schedulerRegistry.addInterval(
        'usersSync',
        setInterval(
          () => this.usersSync(),
          Number(process.env.USER_SYNC_IN_MINUTES) * 60000,
        ),
      );
    }
  }
}
